/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.coreGenerator.base.accessorgen.method;

import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.core.data.AccessorCollection;
import com.inspur.edp.cef.core.data.AccessorReadonlyCollection;
import com.inspur.edp.cef.entity.accessor.entity.IChildAccessor;
import com.inspur.edp.cef.entity.accessor.entity.IEntityAccessor;
import com.inspur.edp.cef.entity.entity.IChildEntityData;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.coreGenerator.base.accessorgen.AccessorUtil;
import com.inspur.edp.cef.generator.coreGenerator.base.entitygen.EntityGeneratorContext;
import com.inspur.edp.cef.generator.coreGenerator.base.entitygen.method.GetResInfoGenerator;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Function;
import java.util.function.Supplier;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;

public class NewChildCollectionGenerator extends ClassMethodGenerator {

  private static String ParName_belong = "belong";
  private final IGspCommonObject node;
  private final EntityGeneratorContext parentContext;
  private final boolean isReadonly;

  public NewChildCollectionGenerator(EntityGeneratorContext parentContext, IGspCommonObject node,
      boolean isReadonly) {
    this.node = node;
    this.parentContext = parentContext;
    this.isReadonly = isReadonly;
  }

  public static String getMethodName(IGspCommonObject node) {
    return "new" + node.getCode() + "Collection";
  }

  @Override
  protected String getMethodName() {
    return getMethodName(node);
  }

  @Override
  protected TypeInfo getReturnType() {
    return new TypeInfo(isReadonly ? AccessorReadonlyCollection.class : AccessorCollection.class);
  }

  @Override
  protected ArrayList<ModifierKeyword> getAccessModifiers() {
    return new ArrayList<>(
        Arrays.asList(ModifierKeyword.PUBLIC_KEYWORD, ModifierKeyword.STATIC_KEYWORD));
  }

  @Override
  protected ArrayList<ParameterInfo> getParameterCollection() {
    return new ArrayList<ParameterInfo>() {{
      add(new ParameterInfo() {{
        setParamName(ParName_belong);
        setParamType(new TypeInfo(IEntityAccessor.class));
      }});
    }};
  }

  @Override
  protected Block buildMethodBody() {
    addImport(IChildEntityData.class.getTypeName());
    addImport(IChildAccessor.class.getTypeName());
    Block block = ast.newBlock();
    ReturnStatement returnStatement = ast.newReturnStatement();
    returnStatement.setExpression(buildNewCollection());
    block.statements().add(returnStatement);
    return block;
  }

  private Expression buildNewCollection() {
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
    classInstanceCreation.setType(
        new TypeInfo(isReadonly ? AccessorReadonlyCollection.class : AccessorCollection.class)
            .getType(ast));
    classInstanceCreation.arguments().add(buildBelongEntityPar());
    classInstanceCreation.arguments().add(buildResInfoPar());
    classInstanceCreation.arguments().add(buildDataSupplierPar());
    classInstanceCreation.arguments().add(buildAccSupplierPar());
    return classInstanceCreation;
  }

  //Function<IChildEntityData, IChildAccessor>
  private Object buildAccSupplierPar() {
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();

    ParameterizedType supplierType = ast.newParameterizedType(
        new TypeInfo(Function.class).getType(ast, parentContext.getProjContext()));
    supplierType.typeArguments().add(new TypeInfo(IChildEntityData.class).getType(ast));
    supplierType.typeArguments().add(new TypeInfo(IChildAccessor.class).getType(ast));
    classInstanceCreation.setType(supplierType);

    AnonymousClassDeclaration classDeclaration = ast.newAnonymousClassDeclaration();
    classDeclaration.bodyDeclarations();
    MethodDeclaration methodDeclaration = ast.newMethodDeclaration();
    methodDeclaration.setName(ast.newSimpleName("apply"));
    methodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
    methodDeclaration.setReturnType2(new TypeInfo(IChildAccessor.class).getType(ast));
    SingleVariableDeclaration applyPar = ast.newSingleVariableDeclaration();
    applyPar.setType(new TypeInfo(IChildEntityData.class).getType(ast));
    String applyParName = "childData";
    applyPar.setName(ast.newSimpleName(applyParName));
    methodDeclaration.parameters().add(applyPar);
    Block methodBlock = ast.newBlock();
    ReturnStatement returnStatement = ast.newReturnStatement();
    returnStatement.setExpression(AccessorUtil
        .buildNewAccExpression(ast, parentContext.getProjContext(), node.getCode(), isReadonly,
            applyParName));
    methodBlock.statements().add(returnStatement);
    methodDeclaration.setBody(methodBlock);
    classDeclaration.bodyDeclarations().add(methodDeclaration);
    classInstanceCreation.setAnonymousClassDeclaration(classDeclaration);
    return classInstanceCreation;
  }

  private Expression buildDataSupplierPar() {
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();

    ParameterizedType supplierType = ast.newParameterizedType(
        new TypeInfo(Supplier.class).getType(ast, parentContext.getProjContext()));
    supplierType.typeArguments().add(new TypeInfo(IChildEntityData.class).getType(ast));
    classInstanceCreation.setType(supplierType);

    AnonymousClassDeclaration classDeclaration = ast.newAnonymousClassDeclaration();
    classDeclaration.bodyDeclarations();
    MethodDeclaration methodDeclaration = ast.newMethodDeclaration();
    methodDeclaration.setName(ast.newSimpleName("get"));
    methodDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
    methodDeclaration.setReturnType2(new TypeInfo(IChildEntityData.class).getType(ast));
    Block methodBlock = ast.newBlock();
    ReturnStatement returnStatement = ast.newReturnStatement();
    ClassInstanceCreation instanceCreation = ast.newClassInstanceCreation();
    instanceCreation.setType(ast.newSimpleType(ast.newName(
        parentContext.getProjContext().getEntityDataMediateTypes().get(node.getCode())
            .getFullName())));
    returnStatement.setExpression(instanceCreation);
    methodBlock.statements().add(returnStatement);
    methodDeclaration.setBody(methodBlock);
    classDeclaration.bodyDeclarations().add(methodDeclaration);
    classInstanceCreation.setAnonymousClassDeclaration(classDeclaration);
    return classInstanceCreation;
  }

  //SalesOrderItemEntity.getResourceInfo()
  private Object buildResInfoPar() {
    MediateType entityType = parentContext.getProjContext().getEntityMediateTypes()
        .get(node.getCode());
    MethodInvocation invocation = ExpressionUtils
        .getMethodInvocation(ast, entityType.getFullName(), GetResInfoGenerator.MethodName);
    return invocation;
  }

  private SimpleName buildBelongEntityPar() {
    return ast.newSimpleName(ParName_belong);
  }

}
